// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TermDictionary.cs" company="Arizona State University">
//   Copyright (C) 2011-2012 Sukru Tikves and Arizona State University
//   
//   Released under Microsoft Public License (Ms-PL)
//   See http://cipsdata.codeplex.com/license for up to date license 
//   informaiton.
// </copyright>
// <summary>
//   The GenericTermDictionary interface.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Cips.DataMining
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;

    /// <summary>
    ///   The GenericTermDictionary interface.
    /// </summary>
    public interface IGenericTermDictionary
    {
        /// <summary>
        /// Gets the <see cref="System.Object"/> at the specified index.
        /// </summary>
        /// <param name="index">
        /// The index.
        /// </param>
        [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:ElementReturnValueMustBeDocumented", Justification = "Reviewed. Suppression is OK here.")]
        [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:ElementParametersMustBeDocumented", Justification = "Reviewed. Suppression is OK here.")]
        object this[int index] { get; }
    }

    /// <summary>
    /// The term dictionary.
    /// </summary>
    /// <typeparam name="T">
    /// The keyword type. 
    /// </typeparam>
    public class TermDictionary<T> : IEnumerable<T>, IGenericTermDictionary
    {
        /// <summary>
        ///   The lookup list.
        /// </summary>
        readonly List<T> lookup = new List<T>();

        /// <summary>
        ///   The terms.
        /// </summary>
        readonly Dictionary<T, int> terms = new Dictionary<T, int>();

        /// <summary>
        ///   The frequency counter.
        /// </summary>
        FrequencyList frequency;

        /// <summary>
        ///   Gets the frequency.
        /// </summary>
        public FrequencyList Frequency
        {
            get { return frequency ?? (frequency = new FrequencyList()); }
        }

        /// <summary>
        /// Does an inverse keyword lookup.
        /// </summary>
        /// <param name="index">
        /// The index.
        /// </param>
        /// <returns>
        /// The keyword at the specified <paramref name="index"/>.
        /// </returns>
        public T this[int index]
        {
            get { return lookup[index]; }
        }

        /// <summary>
        /// Maps the given keyword.
        /// </summary>
        /// <param name="term">
        /// The keyword.
        /// </param>
        /// <returns>
        /// The mapped keyword index.
        /// </returns>
        public int this[T term]
        {
            get
            {
                if (term == null)
                    throw new ArgumentNullException("term");

                int value;
                if (terms.TryGetValue(term, out value))
                    return value;

                if (term is string && term.ToString() == string.Empty)
                    throw new ArgumentException();

                value = terms.Count;

                terms.Add(term, value);
                lookup.Add(term);

                return value;
            }
        }

        /// <summary>
        /// Does an inverse keyword lookup.
        /// </summary>
        /// <param name="index">
        /// The index.
        /// </param>
        /// <returns>
        /// The keyword at the specified <paramref name="index"/>.
        /// </returns>
        object IGenericTermDictionary.this[int index]
        {
            get
            {
                return this[index];
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<T> GetEnumerator()
        {
            return lookup.OrderBy(w => w).GetEnumerator();
        }

        /// <summary>
        /// Gets the terms.
        /// </summary>
        /// <returns>A list of all the terms.</returns>
        public IEnumerable<T> GetTerms()
        {
            return lookup;
        }

        /// <summary>
        /// Maps the specified input terms.
        /// </summary>
        /// <param name="inputTerms">
        /// The input terms. 
        /// </param>
        /// <returns>
        /// List of mapped terms.
        /// </returns>
        public List<int> Map(IEnumerable<T> inputTerms)
        {
            return inputTerms.Select(t => this[t]).ToList();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        ///   The frequency list.
        /// </summary>
        public class FrequencyList : IEnumerable<int>
        {
            /// <summary>
            /// The frequency counts.
            /// </summary>
            readonly List<int> frequency = new List<int>();

            /// <summary>
            /// Gets or sets the frequency at the specified index.
            /// </summary>
            [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:ElementReturnValueMustBeDocumented", Justification = "Reviewed. Suppression is OK here."),
            SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:ElementParametersMustBeDocumented", Justification = "Reviewed. Suppression is OK here.")]
            public int this[int index]
            {
                get
                {
                    if (index >= frequency.Count)
                        frequency.AddRange(Enumerable.Repeat(0, index - frequency.Count + 1));

                    return frequency[index];
                }

                set
                {
                    if (index >= frequency.Count)
                        frequency.AddRange(Enumerable.Repeat(0, index - frequency.Count + 1));

                    frequency[index] = value;
                }
            }

            /// <summary>
            /// Returns an enumerator that iterates through the collection.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
            /// </returns>
            public IEnumerator<int> GetEnumerator()
            {
                return frequency.GetEnumerator();
            }

            /// <summary>
            /// Returns an enumerator that iterates through a collection.
            /// </summary>
            /// <returns>
            /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
            /// </returns>
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    }
}